
/*
 * Copyright (c) 2010 Sergey N Frolov
 * 
 * See LICENSE file
 */

#include "ngx_javascript.h"

/*
 * Set socket non-blocking
 */
int ngx_javascript_server_socket_nonblock(int s) {
	int flags;
	flags = fcntl(s, F_GETFL);
	if (flags < 0) return flags;
	flags |= O_NONBLOCK;
	flags = fcntl(s, F_SETFL, flags);
	if (flags < 0) return flags;
	return 0;
}

/*
 * Create listening socket
 */
int ngx_javascript_server_socket(ngx_javascript_server_t *server) {
	int s, r, flags = 1;
	int l = sizeof(struct sockaddr_un);
	mode_t u;
	struct sockaddr_un su;
	struct sockaddr *sa = (struct sockaddr *) &su;
	su.sun_family = AF_UNIX;
	strcpy(su.sun_path, server->socket_path);
	s = socket(sa->sa_family, SOCK_STREAM, 0);
	if (s < 0) return s;
	r = setsockopt(s, SOL_SOCKET, SO_REUSEADDR, &flags, sizeof(flags));
	if (r < 0) return r;
	unlink(server->socket_path);
	u = umask(0777 ^ server->socket_mode);
	r = bind(s, sa, l);
	if (r < 0) return r;
	chown(server->socket_path, server->uid, server->gid);
	umask(u);
	r = listen(s, -1);
	if (r < 0) return r;
	r = ngx_javascript_server_socket_nonblock(s);
	if (r < 0) return r;
	return s;
}

/*
 * Accept connection
 * This is called from main thread only
 */
void ngx_javascript_server_accept_event(int fd, short event, void *arg) {
	ngx_javascript_server_t *server = (ngx_javascript_server_t *) arg;
	ngx_javascript_client_t *client;
	struct sockaddr_un client_addr;
	socklen_t client_addr_len = sizeof(struct sockaddr_un);
	
	client = ngx_javascript_server_client_slot(server);
	if (client == NULL) return;
	int client_fd = accept(fd, (struct sockaddr *) &client_addr, &client_addr_len);
	if (client_fd < 0) return;
	client->fd = client_fd;
	client->wait = 1;
	event_set(&client->event, client_fd, EV_READ|EV_PERSIST, ngx_javascript_client_read_event, client);
	event_add(&client->event, NULL);
}

/*
 * Server shutdown
 */
void ngx_javascript_server_shutdown(ngx_javascript_server_t *server) {
	ngx_javascript_client_t *client;
	ngx_javascript_worker_t *worker;
	int i;
	for (i = 0; i < server->clients_len; i ++) {
		client = server->clients[i];
		if (client->fd >= 0) ngx_javascript_client_close(client);
		ngx_javascript_client_free(client);
		server->clients[i] = NULL;
	}
	for (i = 0; i < server->workers_len; i ++) {
		worker = server->workers[i];
		if (worker->client != NULL) ngx_javascript_client_close(client);
		ngx_javascript_worker_free(worker);
		server->workers[i] = NULL;
	}
	ngx_javascript_free(server->clients);
	server->clients = NULL;
	ngx_javascript_free(server->workers);
	server->workers = NULL;
	exit(0);
}

/*
 * Handle signal
 */
void ngx_javascript_server_signal_event(int fd, short event, void *arg) {
	ngx_javascript_server_t *server = arg;
	switch (fd) {
		case SIGINT:
			ngx_javascript_server_shutdown(server);
			break;
	}
}
